热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

部类|这个地方_[死磕Spring37/43]IOC之bean的实例化策略:InstantiationStrategy

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[死磕Spring37/43]---IOC之bean的实例化策略:InstantiationStrategy相关的知识,希望对你

篇首语:本文由编程笔记#小编为大家整理,主要介绍了[死磕 Spring 37/43] --- IOC 之 bean 的实例化策略: InstantiationStrategy相关的知识,希望对你有一定的参考价值。


引用原文:
https://www.cmsblogs.com/article/1391375583832838144
[死磕 Spring 37/43] — IOC 之 bean 的实例化策略: InstantiationStrategy

instantiation [ɪnstænʃɪ’eɪʃən] 实例化,例示;


正文

在开始分析 InstantiationStrategy 之前,我们先来简单回顾下 bean 的实例化过程:


  • bean 的创建,主要是 AbstractAutowireCapableBeanFactory.doCreateBean() ,在这个方法中有 bean 的实例化、属性注入和初始化过程,对于 bean 的实例化过程这是根据 bean 的类型来判断的,如果是单例模式,则直接从 factoryBeanInstanceCache 缓存中获取,否则调用 createBeanInstance() 创建。
  • 在 createBeanInstance() 中,如果 Supplier 不为空,则调用 obtainFromSupplier() 实例化 bean。如果 factory 不为空,则调用 instantiateUsingFactoryMethod() 实例化 bean ,如果都不是则调用 instantiateBean() 实例化bean 。但是无论是 instantiateUsingFactoryMethod() 还是 instantiateBean() 最后都一定会调用到 InstantiationStrategy 接口的 instantiate()。

InstantiationStrategy

InstantiationStrategy 接口定义了 Spring Bean 实例化的策略,根据创建对象情况的不同,提供了三种策略:无参构造方法、有参构造方法、工厂方法。如下:

public interface InstantiationStrategy

/**
* 默认构造方法
*/

Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner)
throws BeansException;

/**
* 指定构造方法
*/

Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
Constructor<?> ctor, &#64;Nullable Object... args) throws BeansException;

/**
* 工厂方法
*/

Object instantiate(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner,
&#64;Nullable Object factoryBean, Method factoryMethod, &#64;Nullable Object... args)
throws BeansException;



SimpleInstantiationStrategy

InstantiationStrategy 接口有两个实现类&#xff1a;SimpleInstantiationStrategy 和 CglibSubclassingInstantiationStrategy。SimpleInstantiationStrategy 对以上三个方法都做了简单的实现。

如果是工厂方法实例化&#xff0c;则直接使用反射创建对象&#xff0c;如下&#xff1a;

public Object instantiate(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner,
&#64;Nullable Object factoryBean, final Method factoryMethod, &#64;Nullable Object... args)

try
if (System.getSecurityManager() !&#61; null)
AccessController.doPrivileged((PrivilegedAction<Object>) () ->
ReflectionUtils.makeAccessible(factoryMethod);
return null;
);

else
ReflectionUtils.makeAccessible(factoryMethod);


Method priorInvokedFactoryMethod &#61; currentlyInvokedFactoryMethod.get();
try
currentlyInvokedFactoryMethod.set(factoryMethod);
Object result &#61; factoryMethod.invoke(factoryBean, args);
if (result &#61;&#61; null)
result &#61; new NullBean();

return result;

finally
if (priorInvokedFactoryMethod !&#61; null)
currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);

else
currentlyInvokedFactoryMethod.remove();



// 省略 catch

如果是构造方法实例化&#xff0c;则是先判断是否有 MethodOverrides&#xff0c;如果没有则是直接使用反射&#xff0c;如果有则就需要 CGLIB 实例化对象。如下&#xff1a;

public Object instantiate(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner)
// Don&#39;t override the class with CGLIB if no overrides.
if (!bd.hasMethodOverrides())
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock)
constructorToUse &#61; (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse &#61;&#61; null)
final Class<?> clazz &#61; bd.getBeanClass();
if (clazz.isInterface())
throw new BeanInstantiationException(clazz, "Specified class is an interface");

try
if (System.getSecurityManager() !&#61; null)
constructorToUse &#61; AccessController.doPrivileged(
(PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor);

else
constructorToUse &#61; clazz.getDeclaredConstructor();

bd.resolvedConstructorOrFactoryMethod &#61; constructorToUse;

catch (Throwable ex)
throw new BeanInstantiationException(clazz, "No default constructor found", ex);



return BeanUtils.instantiateClass(constructorToUse);

else
// Must generate CGLIB subclass.
return instantiateWithMethodInjection(bd, beanName, owner);



public Object instantiate(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner,
final Constructor<?> ctor, &#64;Nullable Object... args)

if (!bd.hasMethodOverrides())
if (System.getSecurityManager() !&#61; null)
// use own privileged to change accessibility (when security is on)
AccessController.doPrivileged((PrivilegedAction<Object>) () ->
ReflectionUtils.makeAccessible(ctor);
return null;
);

return (args !&#61; null ? BeanUtils.instantiateClass(ctor, args) : BeanUtils.instantiateClass(ctor));

else
return instantiateWithMethodInjection(bd, beanName, owner, ctor, args);


SimpleInstantiationStrategy 对 instantiateWithMethodInjection() 的实现任务交给了子类 CglibSubclassingInstantiationStrategy。


MethodOverrides

对于 MethodOverrides&#xff0c;如果读者是跟着小编文章一路跟过来的话一定不会陌生&#xff0c;在 BeanDefinitionParserDelegate 类解析 的时候是否还记得这两个方法&#xff1a;parseLookupOverrideSubElements() 和 parseReplacedMethodSubElements() 这两个方法分别用于解析 lookup-method 和 replaced-method。parseLookupOverrideSubElements() 源码如下&#xff1a;

更多关于 lookup-method 和 replaced-method 请看&#xff1a;【死磕 Spring】----- IOC 之解析 bean 标签&#xff1a;meta、lookup-method、replace-method


CGLIB 实例化策略

类 CglibSubclassingInstantiationStrategy 为 Spring 实例化 bean 的默认实例化策略&#xff0c;其主要功能还是对父类功能进行补充&#xff1a;其父类将 CGLIB 的实例化策略委托其实现。

--- SimpleInstantiationStrategy
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner)
throw new UnsupportedOperationException("Method Injection not supported in SimpleInstantiationStrategy");


--- CglibSubclassingInstantiationStrategy
&#64;Override
protected Object instantiateWithMethodInjection(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner)
return instantiateWithMethodInjection(bd, beanName, owner, null);

CglibSubclassingInstantiationStrategy 实例化 bean 策略是通过其内部类 CglibSubclassCreator 来实现的。

protected Object instantiateWithMethodInjection(RootBeanDefinition bd, &#64;Nullable String beanName, BeanFactory owner,
&#64;Nullable Constructor<?> ctor, &#64;Nullable Object... args)
return new CglibSubclassCreator(bd, owner).instantiate(ctor, args);

创建 CglibSubclassCreator 实例然后调用其 instantiate()&#xff0c;该方法用于动态创建子类实例&#xff0c;同时实现所需要的 lookups&#xff08;lookup-method、replace-method&#xff09;。

public Object instantiate(&#64;Nullable Constructor<?> ctor, &#64;Nullable Object... args)
Class<?> subclass &#61; createEnhancedSubclass(this.beanDefinition);
Object instance;
if (ctor &#61;&#61; null)
instance &#61; BeanUtils.instantiateClass(subclass);

else
try
Constructor<?> enhancedSubclassConstructor &#61; subclass.getConstructor(ctor.getParameterTypes());
instance &#61; enhancedSubclassConstructor.newInstance(args);

catch (Exception ex)
throw new BeanInstantiationException(this.beanDefinition.getBeanClass(),
"Failed to invoke constructor for CGLIB enhanced subclass [" &#43; subclass.getName() &#43; "]", ex);



//这个地方解决一个bug&#xff0c;bug提交报告https://jira.spring.io/browse/SPR-10785
// SPR-10785: set callbacks directly on the instance instead of in the
// enhanced class (via the Enhancer) in order to avoid memory leaks.
Factory factory &#61; (Factory) instance;
factory.setCallbacks(new Callback[] NoOp.INSTANCE,
new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner),
new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner));
return instance;

调用 createEnhancedSubclass() 为提供的 BeanDefinition 创建 bean 类的增强子类。

private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition)
// cglib里面的用法&#xff0c;对原始class进行增强&#xff0c;并设置callback
Enhancer enhancer &#61; new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
if (this.owner instanceof ConfigurableBeanFactory)
ClassLoader cl &#61; ((ConfigurableBeanFactory) this.owner).getBeanClassLoader();
enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl));

// 过滤&#xff0c;自定义逻辑来指定调用的callback下标
enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
enhancer.setCallbackTypes(CALLBACK_TYPES);
return enhancer.createClass();

获取子类增强 class 后&#xff0c;如果 Constructor 实例 ctr 为空&#xff0c;则调用默认构造函数&#xff08;BeanUtils.instantiateClass()&#xff09;来实例化类&#xff0c;否则则根据构造函数类型获取具体的构造器&#xff0c;调用 newInstance() 实例化类。在 createEnhancedSubclass() 我们注意两行代码&#xff1a;

enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition));
enhancer.setCallbackTypes(CALLBACK_TYPES);

通过 MethodOverrideCallbackFilter 来定义调用 callback 类型&#xff0c;MethodOverrideCallbackFilter 是用来定义 CGLIB 回调过滤方法的拦截器行为&#xff0c;它继承 CglibIdentitySupport 实现 CallbackFilter 接口&#xff0c; CallbackFilter 是 CGLIB 的一个回调过滤器&#xff0c;CglibIdentitySupport 则为 CGLIB 提供 hashCode() 和 equals() 方法&#xff0c;以确保 CGLIB 不会为每个 bean 生成不同的类。MethodOverrideCallbackFilter 实现 CallbackFilter accept()&#xff1a;

public int accept(Method method)
MethodOverride methodOverride &#61; getBeanDefinition().getMethodOverrides().getOverride(method);
if (logger.isTraceEnabled())
logger.trace("Override for &#39;" &#43; method.getName() &#43; "&#39; is [" &#43; methodOverride &#43; "]");

if (methodOverride &#61;&#61; null)
return PASSTHROUGH;

else if (methodOverride instanceof LookupOverride)
return LOOKUP_OVERRIDE;

else if (methodOverride instanceof ReplaceOverride)
return METHOD_REPLACER;

throw new UnsupportedOperationException("Unexpected MethodOverride subclass: " &#43;
methodOverride.getClass().getName());

根据 BeanDefinition 中定义的 MethodOverride 不同&#xff0c;返回不同的值&#xff0c; 这里返回的 PASSTHROUGH 、LOOKUP_OVERRIDE、METHOD_REPLACER 都是 Callbak 数组的下标&#xff0c;这里对应的数组为 CALLBACK_TYPES 数组&#xff0c;如下&#xff1a;

private static final Class<?>[] CALLBACK_TYPES &#61; new Class<?>[]
NoOp.class, LookupOverrideMethodInterceptor.class, ReplaceOverrideMethodInterceptor.class;

这里又定义了两个熟悉的拦截器 &#xff1a;LookupOverrideMethodInterceptor 和 ReplaceOverrideMethodInterceptor&#xff0c;两个拦截器分别对应两个不同的 callback 业务&#xff1a;


LookupOverrideMethodInterceptor

private static class LookupOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor

private final BeanFactory owner;

public LookupOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner)
super(beanDefinition);
this.owner &#61; owner;


&#64;Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable
// Cast is safe, as CallbackFilter filters are used selectively.
LookupOverride lo &#61; (LookupOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(lo !&#61; null, "LookupOverride not found");
Object[] argsToUse &#61; (args.length > 0 ? args : null); // if no-arg, don&#39;t insist on args at all
if (StringUtils.hasText(lo.getBeanName()))
return (argsToUse !&#61; null ? this.owner.getBean(lo.getBeanName(), argsToUse) :
this.owner.getBean(lo.getBeanName()));

else
return (argsToUse !&#61; null ? this.owner.getBean(method.getReturnType(), argsToUse) :
this.owner.getBean(method.getReturnType()));




ReplaceOverrideMethodInterceptor

private static class ReplaceOverrideMethodInterceptor extends CglibIdentitySupport implements MethodInterceptor

private final BeanFactory owner;

public ReplaceOverrideMethodInterceptor(RootBeanDefinition beanDefinition, BeanFactory owner)
super(beanDefinition);
this.owner &#61; owner;


&#64;Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy mp) throws Throwable
ReplaceOverride ro &#61; (ReplaceOverride) getBeanDefinition().getMethodOverrides().getOverride(method);
Assert.state(ro !&#61; null, "ReplaceOverride not found");
// TODO could cache if a singleton for minor performance optimization
MethodReplacer mr &#61; this.owner.getBean(ro.getMethodReplacerBeanName(), MethodReplacer.class);
return mr.reimplement(obj, method, args);


通过这两个拦截器&#xff0c;再加上这篇博客&#xff1a;【死磕 Spring】----- IOC 之解析 bean 标签&#xff1a;meta、lookup-method、replace-method&#xff0c;是不是一道绝佳的美食。


推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
author-avatar
手机用户2602918231
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有